package cn.demoncat.util.lang;

import cn.demoncat.util.web.AopUtil;
import org.aspectj.lang.JoinPoint;
import org.reflections.ReflectionUtils;

import java.lang.annotation.*;
import java.lang.reflect.AnnotatedElement;
import java.util.ArrayList;
import java.util.List;
import java.util.Set;
import java.util.function.Predicate;

/**
 * 注解工具类
 * 
 * @author 延晓磊
 *
 * @since 2019年8月31日
 */
public class AnnotationUtil {

	/**
	 * 通用注解
	 */
	private static final List<Class<? extends Annotation>> COMMON_ANNOTATIONS = CollectionUtil.toList(
			Target.class, 			// 指定注解的ElementType（位置）
			Retention.class, 		// 指定注解的RetentionPolicy（有效期）
			Inherited.class, 		// 指定注解被子类继承
			Documented.class, 		// 指定注解
			SuppressWarnings.class, // 排除代码编译警告
			SafeVarargs.class,		// 排除可变参数警告
			Override.class, 		// 实现方法
			Deprecated.class		// 警告类或方法过期
		);
	
	
	/**
	 * 获取注解集
	 * 
	 * @param annotated	可注解对象：Class,Method,Field,Param,AnnotationType
	 * 
	 * @return 注解，包含继承的注解、注解类的递归注解
	 * 
	 * @author 延晓磊
	 *
	 * @since 2019年8月31日
	 */
	public static List<Annotation> getAnnotations(AnnotatedElement annotated) {
		List<Annotation> list = new ArrayList<>();
		// 获取注解、注解类的注解
		for (Annotation annotation : annotated.getAnnotations()) {
			getAnnotations(annotation, list);
		}
		return list;
	}

	/**
	 * 递归获取注解的注解集，保存到list
	 * 
	 * @param annotation	注解
	 * @param list			容器
	 * 
	 * @author 延晓磊
	 *
	 * @since 2019年8月31日
	 */
	public static void getAnnotations(Annotation annotation, List<Annotation> list) {
		// 排除通用注解
		if (!COMMON_ANNOTATIONS.contains(annotation.annotationType())) {
			// 添加注解
			list.add(annotation);
			// 获取注解类的注解
			for (Annotation item : annotation.annotationType().getAnnotations()) {
				// 排除通用注解
				if (!COMMON_ANNOTATIONS.contains(item.annotationType())) {
					// 递归
					getAnnotations(item, list);
				}
			}
		}
	}
	
	/**
	 * 获取注解
	 * 
	 * @param annotated	可注解对象：Class,Method,Field,Param,AnnotationType
	 * @param annotationType 注解类型
	 * 
	 * @return 注解，包含继承的注解、注解的递归注解
	 * 
	 * @author 延晓磊
	 *
	 * @since 2019年8月31日
	 */
	public static <T extends Annotation>T getAnnotation(AnnotatedElement annotated, Class<T> annotationType) {
		// 获取注解
		T result = annotated.getAnnotation(annotationType);
		// 未找到，递归获取
		if (result == null) {
			for (Annotation annotation : annotated.getAnnotations()) {
				result = getAnnotation(annotation, annotationType);
				if (result != null) {
					break;
				}
			}
		}
		return result;
	}
	
	/**
	 * 递归获取注解
	 * 
	 * @param annotation
	 * @param annotationType
	 * @return
	 * 
	 * @author 延晓磊
	 *
	 * @since 2019年8月31日
	 */
	public static <T extends Annotation>T getAnnotation(Annotation annotation, Class<T> annotationType) {
		// 排除通用注解
		if (!COMMON_ANNOTATIONS.contains(annotation.annotationType())) {
			// 通过注解类的注解
			T result = annotation.annotationType().getAnnotation(annotationType);
			if (result != null) {
				return result;
			}else {
				// 未找到，递归获取
				for (Annotation item : annotation.annotationType().getAnnotations()) {
					// 排除通用注解
					if (!COMMON_ANNOTATIONS.contains(item.annotationType())) {
						// 递归
						result = getAnnotation(item, annotationType);
						if (result != null) {
							return result;
						}
					}
				}
			}
		}
		return null;
	}
	
	/**
	 * 获取声明的注解集
	 * 
	 * @param type			注解元素
	 * @param predicates	筛选条件
	 * @return
	 * 
	 * @author 延晓磊
	 *
	 * @since 2020年2月1日
	 */
	@SuppressWarnings("unchecked")
	public static <T extends AnnotatedElement> Set<Annotation> getAnnotations(T type, Predicate<Annotation>... predicates) {
        return ReflectionUtils.getAnnotations(type, predicates);
    }
	
	/**
	 * 获取全部注解集
	 * 
	 * @param type			注解元素
	 * @param predicates	筛选条件
	 * @return
	 * 
	 * @author 延晓磊
	 *
	 * @since 2020年2月1日
	 */
	 @SuppressWarnings("unchecked")
	public static <T extends AnnotatedElement> Set<Annotation>  getAllAnnotations(T type, Predicate<Annotation>... predicates) {
        return ReflectionUtils.getAllAnnotations(type, predicates);
    }

	/**
	 * 获取AOP方法的注解
	 *
	 * @param point	AOP切面参数
	 * @param clazz	注解类型
	 * @param <T>
	 * @return
	 */
    public static <T extends Annotation>T getMethodAnnotation(JoinPoint point, Class<T> clazz){
		return AopUtil.getMethodAnnotation(point, clazz);
	}


}
